#!/usr/bin/env python2
#-*- coding: utf-8 -*-

"""
Created on 20151101
"""
import json
import os
import traceback
import threading
import signal
import datetime
import socket

from twisted.internet import reactor, protocol
from twisted.internet.protocol import Factory, Protocol, ClientFactory
from Ump import utils

class SocketClient:

    def __init__(self, host='192.168.120.71', port=27905):
        self.host = host
        self.port = port
        self.ADDR = (host, port)

    def send(self, message):
        socketClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            socketClient.connect(self.ADDR)
            socketClient.send(json.dumps(message))
            socketClient.close()
        except Exception,e:
            raise Exception("管理节点服务未启动")
        finally:
            socketClient.close()

class ServerProtocol(Protocol):
    def __init__(self, sockets, callback):
        self.sockets = sockets
        self.callback = callback

    def connectionMade(self):
        if not self.sockets.has_key(self):
            self.sockets[self] = {}

    def dataReceived(self, msg):
        self.client_host = self.transport.getPeer().host
        self.callback(msg)

    def connectionLost(self, reason):
        if self.sockets.has_key(self):
            del self.sockets[self]

class ServerFactory(protocol.ServerFactory):
    def __init__(self, callback=None):
        self.sockets = {}
        self.callback = callback

    def buildProtocol(self, addr):
        return ServerProtocol(self.sockets, self.callback)

class CloudBusError(Exception):
    pass

class CloudBus(object):
    hostname = os.popen('hostname').read().strip('\n')

    CORRELATION_ID = "correlationId"
    REPLY_TO = "replyTo"
    IS_MESSAGE_REPLY = "isReply"
    NO_NEED_REPLY_MSG = 'noReply'

    class Request(object):
        def __init__(self):
            self.request = None
            self.callback = None

    def _message_handler(self, body):
        try:
            msg = body 
            if not isinstance(body,dict):
                msg = json.loads(body)
            if len(msg.keys()) != 1:
                raise CloudBusError(u'消息格式必须是只有一个键的字典')

            msg_name = msg.keys()[0]
            msg_body = msg.values()[0]
            headers = msg_body.get("ui_info")

            if not msg_name == "QueryDB":
                LOG.info('+++++++++++++++++++++++++++++++++')
                LOG.info('received reply: %s' % body)
                LOG.info('+++++++++++++++++++++++++++++++++')

            if not headers.has_key(self.IS_MESSAGE_REPLY):
                raise CloudBusError('received none message reply: %s' % body)

            correlation_id = headers.get(self.CORRELATION_ID)
            if not correlation_id:
                raise CloudBusError('cannot find correlationId in headers, invalid reply: %s' % body)

            req = self.requests.get(correlation_id)
            if not req:
                raise CloudBusError('cannot find request[id:%s], drop reply: %s' % (correlation_id, req))

            req.callback(msg)
            del self.requests[correlation_id]
        except:
            traceback.print_exc()

    def stop(self):
        self.should_stop = True
        self.reply_consumer_thread.join()

    def __init__(self, options):
        self.options = options
        self.uuid = utils.uuid4()
        self.requests = {}
        self.should_stop = False
        self.port = utils._random_free_port()

        def start_reply_consuming():
            LOG.info('reply consumer thread starts : %s' % self.port)
            ''' exec complete receive'''
            reactor.listenTCP(self.port, ServerFactory(self._message_handler))
            reactor.run(installSignalHandlers=False)

        try:
            self.reply_consumer_thread = threading.Thread(target=start_reply_consuming)
            self.reply_consumer_thread.setDaemon(True)
            self.reply_consumer_thread.start()
        except Exception as ce:
            traceback.print_exc(file=sys.stdout)

    def send(self, msg_str, callback):
        try:
            msg = msg_str
            if not isinstance(msg_str,dict):
                msg = json.loads(msg_str)

            if len(msg.keys()) != 1:
                raise CloudBusError(u'消息格式必须是只有一个键的字典')

            msg_name = msg.keys()[0]
            msg_body = msg.values()[0]

            mid = msg_body['id'] = utils.uuid4()
            msg_body['serviceId'] = "api.portal"

            headers = {
                self.CORRELATION_ID: mid,
                self.REPLY_TO: self.port,
                self.NO_NEED_REPLY_MSG: 'false'
            }
            msg_body['ui_info'] = headers

            req = self.Request()
            req.callback = callback
            req.request = msg
            self.requests[mid] = req
            if not msg_name == "QueryDB":
                LOG.info('%'*10)
                LOG.info("send message %s" % msg)
                LOG.info('%'*10)
            port = 27914
            SocketClient('0.0.0.0', port).send(msg)

        except Exception,e:
            traceback.print_exc()
            raise Exception(e)
            raise CloudBusError("invalid JSON format: %s" % msg_str)

#    def call(self, msg_str):
#        cond = threading.Condition()
#
#        ret = {
#            'done' : False,
#            'reply': None
#        }
#
#        def cb(reply):
#            cond.acquire()
#            try:
#                ret['done'] = True
#                ret['reply'] = reply.values()[0]
#                cond.notifyAll()
#            finally:
#                cond.release()
#
#        cond.acquire()
#        try:
#            self.send(msg_str, cb)
#            while not ret['done']:
#                cond.wait()
#        finally:
#            cond.release()
#
#        return ret['reply']


    def call(self, msg_str, timeout=300):
        cond = threading.Condition()

        ret = {
            'done' : False,
            'reply': None
        }

        def cb(reply):
            cond.acquire()
            try:
                ret['done'] = True
                ret['reply'] = reply
                cond.notifyAll()
            finally:
                cond.release()

        cond.acquire()
        try:
            self.send(msg_str, cb)

            start_time = datetime.datetime.now()
            delta = datetime.timedelta(seconds=timeout)
            while not ret['done']:
                now_time = datetime.datetime.now()
                if now_time - start_time > delta:
                    raise Exception('超时退出进程')
                if timeout < 10:
                    cond.wait(1)
                else:
                    cond.wait(1)
        finally:
            cond.release()

        return ret['reply']

class Server(object):
    class Receipt(object):
        PROCESSING = 1
        DONE = 2

        def __init__(self):
            self.id = utils.uuid4()
            self.rsp = None
            self.status = self.PROCESSING

        def to_json(self):
            return json.dumps(self.__dict__)

    def __init__(self, timeout=20*60):
        self.options = None
        self.bus = CloudBus(self.options)
        self.api_tasks = {}
        self.timeout = timeout

        def exit(signal, frame):
            os._exit(0)

        signal.signal(signal.SIGINT, exit)

    def stop(self):
        self.bus.stop()

    def api_sync_call(self, msg_str):
        try:
            reply = self.bus.call(msg_str, timeout=self.timeout)
            return json.dumps(reply)
        except Exception as e:
            traceback.print_exc()
            LOG.info(utils.get_exception_stacktrace())
            return str(e), 400

if __name__ == "__main__":
    server = Server()
    body={"SyncCluster":{"params":{"cluster_id":'ALL'}}}
    reply = server.api_sync_call(body)
